package com.bg.bgadmin.common.utils.excel;

import com.bg.bgadmin.common.utils.DateUtils;
import com.bg.bgadmin.common.utils.Encodes;
import com.bg.bgadmin.common.utils.Reflections;
import com.bg.bgadmin.common.utils.excel.annotation.ExcelField;
import com.bg.bgadmin.modules.sys.utils.DictUtils;
import com.google.common.collect.Lists;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

public class ExportCsv {

    private static Logger log = LoggerFactory.getLogger(ExportCsv.class);
    /**
     * 注解列表（Object[]{ ExcelField, Field/Method }）
     */
    List<Object[]> annotationList = Lists.newArrayList();

    List<String> headlist = Lists.newArrayList();
    List<String[]> datalist= Lists.newArrayList();
    /**
     * 构造函数
     *
     * @param cls 实体对象，通过annotation.ExportField获取标题
     */
    public ExportCsv(Class<?> cls){
        this( cls, 1);
    }

    /**
     * 构造函数
     *
     * @param cls 实体对象，通过annotation.ExportField获取标题
     * @param type 导出类型（1:导出数据；2：导出模板）
     * @param groups 导入分组
     */
    public ExportCsv(Class<?> cls, int type, int... groups){
        // Get annotation field
        Field[] fs = cls.getDeclaredFields();
        for (Field f : fs){
            ExcelField ef = f.getAnnotation(ExcelField.class);
            if (ef != null && (ef.type()==0 || ef.type()==type)){
                if (groups!=null && groups.length>0){
                    boolean inGroup = false;
                    for (int g : groups){
                        if (inGroup){
                            break;
                        }
                        for (int efg : ef.groups()){
                            if (g == efg){
                                inGroup = true;
                                annotationList.add(new Object[]{ef, f});
                                break;
                            }
                        }
                    }
                }else{
                    annotationList.add(new Object[]{ef, f});
                }
            }
        }
        // Get annotation method
        Method[] ms = cls.getDeclaredMethods();
        for (Method m : ms){
            ExcelField ef = m.getAnnotation(ExcelField.class);
            if (ef != null && (ef.type()==0 || ef.type()==type)){
                if (groups!=null && groups.length>0){
                    boolean inGroup = false;
                    for (int g : groups){
                        if (inGroup){
                            break;
                        }
                        for (int efg : ef.groups()){
                            if (g == efg){
                                inGroup = true;
                                annotationList.add(new Object[]{ef, m});
                                break;
                            }
                        }
                    }
                }else{
                    annotationList.add(new Object[]{ef, m});
                }
            }
        }
        // Field sorting
        Collections.sort(annotationList, new Comparator<Object[]>() {
            public int compare(Object[] o1, Object[] o2) {
                return new Integer(((ExcelField)o1[0]).sort()).compareTo(
                        new Integer(((ExcelField)o2[0]).sort()));
            };
        });
        // Initialize
        List<String> headerList = Lists.newArrayList();
        for (Object[] os : annotationList){
            String t = ((ExcelField)os[0]).title();
            // 如果是导出，则去掉注释
            if (type==1){
                String[] ss = StringUtils.split(t, "**", 2);
                if (ss.length==2){
                    t = ss[0];
                }
            }
            headerList.add(t);
        }
        initialize( headerList);
    }

    /**
     * 构造函数
     *
     * @param headers 表头数组
     */
    public ExportCsv(String[] headers) {
        initialize( Lists.newArrayList(headers));
    }

    /**
     * 构造函数
     *
     * @param headerList 表头列表
     */
    public ExportCsv(List<String> headerList) {
        initialize( headerList);
    }

    /**
     * 初始化函数
     *
     * @param headerList 表头列表
     */
    private void initialize(List<String> headerList) {
        this.headlist=headerList;
    }
    /**
     * 添加数据（通过annotation.ExportField添加数据）
     * @return list 数据列表
     */
    public <E> ExportCsv setDataList(List<E> list){
        for (E e : list){
            StringBuilder sb = new StringBuilder();
            List<Object> recordRowList = Lists.newArrayList();
            for (Object[] os : annotationList){
                ExcelField ef = (ExcelField)os[0];
                Object val = null;
                // Get entity value
                try{
                    if (StringUtils.isNotBlank(ef.value())){
                        val = Reflections.invokeGetter(e, ef.value());
                    }else{
                        if (os[1] instanceof Field){
                            val = Reflections.invokeGetter(e, ((Field)os[1]).getName());
                        }else if (os[1] instanceof Method){
                            val = Reflections.invokeMethod(e, ((Method)os[1]).getName(), new Class[] {}, new Object[] {});
                        }
                    }
                    // If is dict, get dict label
                    if (StringUtils.isNotBlank(ef.dictType())){
                        val = DictUtils.getDictLabel(val==null?"":val.toString(), ef.dictType(), "");
                    }
                }catch(Exception ex) {
                    // Failure to ignore
                    log.info(ex.toString());
                    val = "";
                }
                this.addObject(recordRowList,val, ef.align(), ef.fieldType());
                sb.append(val + ", ");
            }
            log.debug("Write success: "+sb.toString());
            datalist.add(recordRowList.toArray(new String[recordRowList.size()]));
        }
        return this;
    }
    public Object addObject(List recordRowList, Object val, int align, Class<?> fieldType){

        Object object=null;
        try {
            if(val == null){
                object="";
            }else if(fieldType != Class.class){
                object=(String)fieldType.getMethod("setValue", Object.class).invoke(null, val);
            }else{
                if(val instanceof String) {
                    object=(String) val;
                }else if(val instanceof Integer) {
                    object=(Integer) val;
                }else if(val instanceof Long) {
                    object=(Long) val;
                }else if(val instanceof Double) {
                    object=(Double) val;
                }else if(val instanceof Float) {
                    object=(Float) val;
                }else if(val instanceof Date) {
                    object= DateUtils.formatDateTime((Date)val) ;
                }else if(val instanceof BigDecimal){
                    object=val.toString();
                }else {
                    object=(String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
                            "fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val);
                }
            }
        } catch (Exception ex) {

            object=val.toString();
        }
        recordRowList.add(object);
        return object;
    }

    /**
     * 输出数据流
     * @param os 输出数据流
     */
    public ExportCsv write(Writer os)  {
        CSVPrinter csvFilePrinter = null;
        //创建 CSVFormat
        CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(headlist.toArray(new String[headlist.size()]));
        //初始化 CSVPrinter
        try {
            csvFilePrinter = new CSVPrinter(os, csvFileFormat);
            csvFilePrinter.printRecords(datalist);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                os.flush();
                os.close();
                csvFilePrinter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return this;
    }

    /**
     * 输出到客户端
     * @param fileName 输出文件名
     */
    public ExportCsv write(HttpServletResponse response, String fileName) throws IOException{
        OutputStreamWriter osw = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
        response.reset();
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename="+ Encodes.urlEncode(fileName));
       /* osw.write(new String(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF }));*/
        osw.write('\ufeff');
        write(osw);
        return this;
    }

    /**
     * 输出到文件
     * @param name 输出文件名
     */
    public ExportCsv writeFile(String name) throws FileNotFoundException, IOException{
        FileWriter fileWriter = null;
        //初始化FileWriter
        fileWriter = new FileWriter(name);
        this.write(fileWriter);
        fileWriter.close();
        return this;
    }
    public static void main(String[] args) throws Throwable {
        List<String[]> data = Lists.newArrayList();
        data.add(new String[]{ "A", "B", "C" });
        data.add(new String[]{ "1", "2", "3" });
        data.add(new String[]{ "A1", "B2", "C3" });
        CSVPrinter csvFilePrinter = null;
        FileWriter fileWriter = new FileWriter("D:\\testuserwww.csv");
        //创建 CSVFormat
        CSVFormat csvFileFormat = CSVFormat.DEFAULT.withRecordSeparator("\n");
        //初始化 CSVPrinter
        csvFilePrinter = new CSVPrinter(fileWriter, csvFileFormat);
        csvFilePrinter.printRecords(data);
        fileWriter.flush();
        fileWriter.close();
        csvFilePrinter.close();
    }
}
